package org.wendu.wdoa.common.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.wendu.wdoa.common.security.model.User;
import org.wendu.wdoa.common.CurrentUser;
import org.wendu.wdoa.common.Result;

import org.wendu.wdoa.common.token.TokenUtils;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

@Slf4j
@Configuration
public class SecurityConfig {
    //配置密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return new BCryptPasswordEncoder();
        //自定义密码编码器，不对密码做任何处理
        return new PasswordEncoder() {

            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword.toString();
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return rawPassword.toString().equals(encodedPassword);
            }
        };
    }

    @Autowired
    private SecurityDao securityDao;

    UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        try {
            //如果userId为令牌
            CurrentUser currentUser = TokenUtils.getCurrentUser(userId);
            log.debug("接收到账号为token,直接解析出用户信息，凭证即token：{}",userId);
            return new CurrentUser(userId,currentUser.getUserName(),userId);
        } catch (Exception e) {
            //userId不是令牌
            User user = securityDao.findUserByUserId(userId);
            log.debug("账号非token，通过数据库查询用户信息，凭证为密码：{}",user.getU_pwd());
            return new CurrentUser(user.getU_id(), user.getU_name(), user.getU_pwd());
        }
    }

    @Bean
    public UserDetailsService userDetailsService() {

    /*
        匿名实现UserDetailsService接口，
        该接口中仅有一个方法
        public UserDetails loadUserByUsername(String s)
        throws UsernameNotFoundException,
        此方法的功能是依据登录用户名（可以理解为账号）加载当前用户信息，
        返回UserDetials接口对象表示当前用户信息
    */
        return this::loadUserByUsername;
//        return userId -> {
//
//            try {
//                //如果userId为令牌
//                CurrentUser currentUser = TokenUtils.getCurrentUser(userId);
//                log.debug("接收到账号为token,直接解析出用户信息，凭证即token：{}",userId);
//                return new CurrentUser(userId,currentUser.getUserName(),userId);
//            } catch (Exception e) {
//                //userId不是令牌
//                User user = securityDao.findUserByUserId(userId);
//                log.debug("账号非token，通过数据库查询用户信息，凭证为密码：{}",user.getU_pwd());
//                return new CurrentUser(user.getU_id(), user.getU_name(), user.getU_pwd());
//            }
//
//        };
    }

    @Bean
    WebSecurityCustomizer webSecurityCustomizer() {
        return new WebSecurityCustomizer() {
            @Override
            public void customize(WebSecurity web) {
                web.ignoring().antMatchers("/pub/**");
            }
        };
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {


        http.authorizeRequests()
                //.antMatchers("/pub/**").permitAll()
                .anyRequest().authenticated();//其它请求需要认证

        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);//新增


        //新增令牌校验过滤器配置
        http.addFilter(new TokenVerifyFilter(authenticationManager()));


        http.logout() //退出配置
                .logoutUrl("/security/logout")//退出地址
                .logoutSuccessHandler((req, resp, authentication) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    ObjectMapper objectMapper = new ObjectMapper();
                    out.write(objectMapper.writeValueAsString(Result.OK()));
                    out.flush();
                })//退出成功的处理器
                .permitAll();//退出地址一律放行

        http.exceptionHandling()

                //未认证用户访问需要认证资源异常处理
                .authenticationEntryPoint((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    ObjectMapper objectMapper = new ObjectMapper();
                    out.print(objectMapper.writeValueAsString(
                            Result.err(Result.CODE_ERR_UNLOGINED,
                                    "请登录！")));
                    out.flush();
                })
                //认证用户访问资源权限不足异常处理
                .accessDeniedHandler((httpServletRequest, httpServletResponse, e) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    PrintWriter out = httpServletResponse.getWriter();
                    ObjectMapper objectMapper = new ObjectMapper();
                    out.print(objectMapper.writeValueAsString(Result.err(Result.CODE_ERR_BUSINESS,
                            "权限不足！")));
                    out.flush();
                });

        //允许跨域，如果springboot/springmvc已有跨域配置，自动采用springboot/springmvc跨域配置
        //http.cors();//由于网关已设置了跨域，所以此处无需配置

        //禁用防CSRF攻击
        http.csrf().disable();

        //配置禁用防网页在iframe显示
        http.headers().frameOptions().disable();

        //基于Token不需要使用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        return http.build();
    }

    @Autowired
    private AuthenticationConfiguration authenticationConfiguration;



    @Bean
    public AuthenticationManager authenticationManager() throws Exception{
        log.debug("authenticationManager================>>>>authenticationManager()");
        AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
        return authenticationManager;
    }

    LoginFilter loginFilter() throws Exception {
        log.debug("loginFilter==============================>>>>>loginFilter()");
        LoginFilter filter = new LoginFilter();
        filter.setAuthenticationManager(authenticationManager());
        filter.setFilterProcessesUrl("/login");
        //处理登录成功
        filter.setAuthenticationSuccessHandler(this::onAuthenticationSuccess);
        //处理登录失败
        filter.setAuthenticationFailureHandler(this::onAuthenticationFailure);
        filter.setUsernameParameter("u_id");
        filter.setPasswordParameter("u_pwd");
        return filter;
    }
    private void print(HttpServletResponse response, Result result) throws IOException {
        response.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8).toString());
        PrintWriter out = response.getWriter();
        ObjectMapper objectMapper = new ObjectMapper();
        out.print(objectMapper.writeValueAsString(result));
        out.flush();
        out.close();
    }
    private void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {


        CurrentUser currentUser = (CurrentUser) authentication.getPrincipal();
log.debug("currentUser==========={},{}",currentUser.getUserId(),currentUser.getUserName());

        CurrentUser tokenUser = new CurrentUser(currentUser.getUserId(), currentUser.getUserName());
        String token = TokenUtils.loginSign(tokenUser,currentUser.getPassword());
        UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;

        currentUser.setPassword(token);

        print(response, Result.OK("登录成功！",token));

    }

    private void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.error("登录失败！", exception);
        print(response, Result.err(Result.CODE_ERR_BUSINESS, "登录失败！"));

    }
}
